package ngat.oss.simulation;

import java.util.*;
import java.io.*;
import java.text.*;

import ngat.util.*;
import ngat.phase2.*;
import ngat.astrometry.*;
import ngat.util.logging.*;

public class BasicChargeAccountingModel implements ChargeAccountingModel, PropertiesConfigurable {

    private double offsetCost;
    private double slewCost;
    private double configCost;
    private double readoutCost;

    private LogProxy logger;

    private NumberFormat nf = NumberFormat.getInstance();

    public BasicChargeAccountingModel() {
	Logger slogger = LogManager.getLogger("SIM");
        logger = new LogProxy("BCAM","",slogger);
	nf.setMaximumFractionDigits(3);
	nf.setGroupingUsed(false);
    }


    public void configure(ConfigurationProperties config) throws Exception {
	// NOTE these are all in seconds ...
	offsetCost  = config.getDoubleValue("offset.cost");
	slewCost    = config.getDoubleValue("slew.cost");
	configCost  = config.getDoubleValue("config.cost");
	readoutCost = config.getDoubleValue("readout.cost");

    }

    /** Returns the cost for executing the group under specified conditions (millis).*/
    public double calculateCost(Group group, EnvironmentSnapshot env) {
	
        int countObs       = 0; // counts obs.
        int countSlews     = 1; // counts distinct source moves - include initial slew onto (first) target
	int countOffsets   = 0; // counts mosaic offsets
	int countExposures = 0; // counts exposures
	int countConfigs   = 0; // counts config changes

	double sumExpose  = 0.0;

        Observation observation = null;
        Source      source      = null;
        Source  prevSource      = null;
        Position    target      = null;
        Position    prevTarget  = null;
	InstrumentConfig instConfig = null;
        InstrumentConfig prevConfig = null;

        // check for sequencing here and create it if it doesnt exist...
        Vector v = null;
        v = group.getSequence();

        if (v == null || v.size() == 0) {
            v = new Vector();
            Iterator it = group.listAllObservations();
            while (it.hasNext()) {
                observation = (Observation)it.next();
                v.add(observation.getName());
            }
        }

        int nseq = v.size();

        Iterator is = v.iterator();
        while (is.hasNext()) {
            String obsName = (String)is.next();
            observation = group.findObservation(obsName);

            countObs++;

            instConfig = observation.getInstrumentConfig();

            source      = observation.getSource();
            target      = source.getPosition();

            // If this is NOT first obs. There IS a previous source.
            if (countObs != 1) {
                if (source != prevSource) {
                    // New source. Add Slew time
		    countSlews++;
		}

		if (! (instConfig.sameAs(prevConfig))) {
		    // New config. Add cfg time
		    countConfigs++;
		}
            }
            prevSource = source;
            prevTarget = target;
	    prevConfig = instConfig;

            // Add up all the contributions to this Obo's time.

            // Mosaics.
            int mosaic = 1;
            switch (observation.getMosaic().getPattern()) {
            case Mosaic.ARRAY:
                mosaic = observation.getMosaic().getCellsRA()*observation.getMosaic().getCellsDec();
                break;
            case Mosaic.SINGLE:
                mosaic = 1;
                break;
            case Mosaic.VERTICAL:
            case Mosaic.HORIZONTAL:
            case Mosaic.SLOPE_UP:
            case Mosaic.SLOPE_DOWN:
                mosaic = 3;
                break;
            case Mosaic.CROSS:
            case Mosaic.X:
                mosaic = 5;
                break;
            case Mosaic.HOLE:
                mosaic = 8;
                break;
            case Mosaic.GRID:
                mosaic = 9;
                break;
            default:
                mosaic = 1;
            }

	    // dont count the first one
	  
            // Multruns.
            int multruns = observation.getNumRuns();

	    countOffsets += (mosaic-1);	   
	    countExposures += multruns*mosaic;

	    // convert to sec for sum	    
	    sumExpose  += mosaic*multruns*observation.getExposeTime()/1000.0;

        } // per observation.

	// return as millis.
	double cost = 1000.0*(countOffsets*offsetCost + 
			      countExposures*readoutCost + 
			      sumExpose +
			      countConfigs*configCost + 
			      countSlews*slewCost);
	
	// these are ALL in seconds
	logger.method("calculateCost(G, env)");
	logger.log(2, "Charge breakdown: "+
                   "Slews   ["+countSlews+" @ "+slewCost+" = "+(countSlews*slewCost)+"]"+
                   ", Offsets ["+countOffsets+" @ "+offsetCost+" = "+(countOffsets*offsetCost)+"]"+
                   ", Readouts["+countExposures+" @ "+readoutCost+" = "+(countExposures*readoutCost)+"]"+
                   ", Configs ["+countConfigs+" @ "+configCost+" = "+(countConfigs*configCost)+"]"+
                   ", Exposing ["+sumExpose+"]"+
                   " Total="+nf.format(cost/1000.0)+"sec / TCH "+nf.format(cost/3600000.0));
	
	// Should also workout ag-aqs inst-aqs and rot CP resets.

	return cost;

    }

}
